
package com.zy.je.common.mybatis.ext.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zy.je.common.core.constant.StringPool;
import com.zy.je.common.core.constant.SysConstant;
import com.zy.je.common.core.service.CommonService;
import com.zy.je.common.core.utils.CheckUtil;
import com.zy.je.common.core.web.rest.ResultStatus;
import com.zy.je.common.mybatis.ext.model.base.BaseTreeEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @author zy
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public abstract class BizTreeServiceImpl< M extends BaseMapper<T>, T extends BaseTreeEntity> extends BizServiceImpl<M, T> implements CommonService {


    /**
     * 树节点操作
     *
     * @param supplier
     * @param function
     * @return
     */
    public <R> R doTreeNode(Supplier<T> supplier, Function<T, R> function) {
        // 获取修改前的parentIds，用于更新子节点的parentIds
        T entity = supplier.get();
        buildNodeParent(entity);
        R ret = function.apply(entity);
        updateSubNodeParentIds(entity);
        return ret;
    }

    /**
     * 构建上级节点信息
     *
     * @param entity
     */
    private void buildNodeParent(T entity) {
        if (null == entity) {
            return;
        }
        // 1.如果更新操作，判断是否更新了上级id
        // 1.1.如果没更新上级id，不用处理
        // 1.2.如果更新了上级id，则需要重新计算上级id的拼串
        // 2.如果是插入操作，判断是否已录入了上级id
        // 2.1.如果未录入上级id，设置默认上级为根节点，并重新计算上级id的拼串
        // 2.2.如果已录入了上级id，重新计算上级id的拼串

        // 如果是更新操作，且parentId为null,则为没更新上级的情况，这种不用处理
        if (!CheckUtil.isBlank(entity.getId()) && null == entity.getParentId()) {
            return;
        }

        // 如果没有设置父节点，则代表为跟节点，有则获取父节点实体
        if (CheckUtil.isBlank(entity.getParentId()) || SysConstant.Tree.ROOT_ID.getValue().equals(entity.getParentId())) {
            entity.setParentId(SysConstant.Tree.ROOT_ID.getValue());
        }

        // 设置新的父节点串
        entity.setParentIds(findParentIds(entity.getParentId()));
    }

    /**
     * 通过当前节点上级id取当前节点的上级id拼串
     *
     * @param parentId
     * @return
     */
    private String findParentIds(String parentId) {
        if (CheckUtil.isBlank(parentId) || SysConstant.Tree.ROOT_ID.getValue().equals(parentId)) {
            return StringPool.COMMA + SysConstant.Tree.ROOT_ID.getValue() + StringPool.COMMA;
        }

        BaseTreeEntity parent = this.getById(parentId);
        CheckUtil.checkBlank(parent, ResultStatus.RECORD_NOT_EXIST);

        StringBuilder sb = new StringBuilder();
        if (CheckUtil.isBlank(parent.getParentIds())) {
            sb.append(StringPool.COMMA);
            sb.append(SysConstant.Tree.ROOT_ID.getValue());
            sb.append(StringPool.COMMA);
        } else {
            sb.append(parent.getParentIds());
        }
        sb.append(parentId);
        sb.append(StringPool.COMMA);

        return sb.toString();
    }

    /**
     * 更新下级节点中上级id的拼串
     *
     * @param entity
     */
    private void updateSubNodeParentIds(T entity) {
        // 如果上级id为null，表示上级未发生变化，这种情况不用更新子节点的上级id拼串
        if (null == entity || null == entity.getParentIds()) {
            return;
        }

        List<T> subNodeList = this.findSubNode(entity);
        String targetId = StringPool.COMMA + entity.getId() + StringPool.COMMA;
        for (T n : subNodeList) {
            // 更新子节点 parentIds
            // 如果不考虑性能，这里重新去拿一次当前节点的上级id拼串好了
            // String targetParentIds = findParentIds(entity.getParentId());
            String targetParentIds = n.getParentIds();
            String newParentIds = entity.getParentIds()
                    + targetParentIds.substring(targetParentIds.indexOf(targetId) + 1, targetParentIds.length());
            updateNodeParentIds(n.getId(), newParentIds);
        }
    }

    /**
     * 查找所有下级节点
     *
     * @param entity
     * @return
     */
    public List<T> findSubNode(T entity) {
        if (null == entity || CheckUtil.isBlank(entity.getId())) {
            return Collections.emptyList();
        }
        String targetId = StringPool.COMMA + entity.getId() + StringPool.COMMA;

        LambdaQueryWrapper<T> query = Wrappers.lambdaQuery(super.getEntityClass());
        query.like(T::getParentIds, targetId);
        return this.list(query);
    }

    /**
     * 通过id更新此节的上级id拼串
     *
     * @param id
     * @param newParentIds
     */
    private void updateNodeParentIds(Serializable id, String newParentIds) {
        this.update(
                Wrappers.lambdaUpdate(super.getEntityClass())
                        .eq(T::getId, id)
                        .set(T::getParentIds, newParentIds)
        );
    }
}
